#include <GL/glew.h>
#include "CShader.h"
#include <iostream>
#include <fstream>
CShader::CShader(const string &vertFile,const string &fragFile) {
	vertSource = textFileRead(vertFile);
	fragSource = textFileRead(fragFile);
	linkShader();
}
string CShader::textFileRead(const string& fn){
	string fileString="";
	ifstream fout;
	char temp[256];
	fout.open(fn);
	if(!fout){
		cout<<"can not find: "<<fn<<endl;
		exit(1);
	}
	while(!fout.eof()){
		fout.getline(temp,255);
		fileString.append(temp);
		fileString+="\n";
	}
	return fileString;
}
void CShader::linkShader()
{
	glewInit();
	vertID = glCreateShader(GL_VERTEX_SHADER);
	fragID = glCreateShader(GL_FRAGMENT_SHADER);
	const char *t1 = vertSource.c_str();
	const char *t2 = fragSource.c_str();
	glShaderSource(vertID,1,&t1,NULL);
	glShaderSource(fragID,1,&t2,NULL);
	glCompileShader(vertID);
	glCompileShader(fragID);
	printShaderInfo(vertID);
	printShaderInfo(fragID);
    shaderID = glCreateProgram();
	glAttachShader(shaderID,vertID);
	glAttachShader(shaderID,fragID);
	glLinkProgram(shaderID);
}
void CShader::printProgramInfo(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 1){
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}
void CShader::printShaderInfo(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
    GLint compiled;
    glGetShaderiv(obj,GL_COMPILE_STATUS,&compiled);
	glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 1){
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}
void CShader::setUniformf(const string &uniformName,int singleSize,int arraSize,const float *value){
	GLint temp = glGetUniformLocation(shaderID,uniformName.c_str());
	if(temp == -1){
	//	fprintf(stderr,"not found %s from glsl shader!\n",uniformName.c_str());
	//	exit(-1);
		return;
	}
	switch(singleSize){
	case 1:
		glUniform1fv(temp,arraSize,value);
		break;
	case 2:
		glUniform2fv(temp,arraSize,value);
		break;
	case 3:
		glUniform3fv(temp,arraSize,value);
		break;
	case 4:
		glUniform4fv(temp,arraSize,value);
		break;
	default:
		return;
	}
}
void CShader::setUniformi(const string &uniformName,int singleSize,int arraSize,const int *value){
	GLint temp = glGetUniformLocation(shaderID,uniformName.c_str());
	if(temp == -1){
	//	fprintf(stderr,"not found %s from glsl shader!\n",uniformName.c_str());
	//	exit(-1);
		return;
	}
	switch(singleSize){
	case 1:
		glUniform1iv(temp,arraSize,value);
		break;
	case 2:
		glUniform2iv(temp,arraSize,value);
		break;
	case 3:
		glUniform3iv(temp,arraSize,value);
		break;
	case 4:
		glUniform4iv(temp,arraSize,value);
		break;
	default:
		return;
	}
}
void CShader::setTexture(const string &texName,int texID,int bindTexIdx,GLuint textureType){
	++bindTexIdx;
	glActiveTexture(GL_TEXTURE0 + bindTexIdx);
	glBindTexture(textureType,texID);
	setUniformi(texName,1,1,&bindTexIdx);
	glActiveTexture(GL_TEXTURE0+ bindTexIdx+1);
}
void CShader::setUniformMaxtrixf(const string &uniformName,int m,int count ,bool transpose,const float *values){
	GLint location  = glGetUniformLocation(shaderID,uniformName.c_str());
	if(location == -1){
		//fprintf(stderr,"not found %s from glsl shader!\n",uniformName.c_str());
		//exit(-1);
		return;
	}
	switch(m){
	case 2:
		glUniformMatrix2fv(location,count,transpose,values);
		break;
	case 3:
		glUniformMatrix3fv(location,count,transpose,values);
		break;
	case 4:
		glUniformMatrix4fv(location,count,transpose,values);
		break;
	default:
		break;
	}
}
void CShader::setVertexAttribf(const string& attribName,int singleSize,const float* value){
	GLint location = glGetAttribLocation(shaderID,attribName.c_str());
	if(location == -1){
		return;
	}
	switch(singleSize){
	case 1:
		glVertexAttrib1fv(location,value);
		break;
	case 2:
		glVertexAttrib2fv(location,value);
		break;
	case 3:
		glVertexAttrib3fv(location,value);
		break;
	case 4:
		glVertexAttrib4fv(location,value);
		break;
	default:
		break;
	}
}
void CShader::beginShader()
{
	glUseProgram(shaderID);
	printProgramInfo(shaderID);
}
void CShader::endShader()
{
	glUseProgram(0);
}
CShader::~CShader() {
	glDetachShader(shaderID,vertID);
	glDetachShader(shaderID,fragID);
	glDeleteShader(vertID);
	glDeleteShader(fragID);
	glDeleteProgram(shaderID);
	// TODO Auto-generated destructor stub
}
